#!/bin/sh
# shellcheck shell=dash disable=SC2169,SC2034,SC3010,SC3028,SC3015 source=/dev/null
#
# Startup script checking for RF capabale hardware
#

# function to identify rf hardware
identify_rf_hardware() {

  # setup default HM RF variables
  HM_HMRF_DEV=""
  HM_HMRF_DEVNODE=""
  HM_HMRF_DEVTYPE=""
  HM_HMRF_ADDRESS=""
  HM_HMRF_ADDRESS_ACTIVE=""
  HM_HMRF_SERIAL=""
  HM_HMRF_VERSION=""
  HM_HMIP_DEV=""
  HM_HMIP_DEVNODE=""
  HM_HMIP_DEVTYPE=""
  HM_HMIP_ADDRESS=""
  HM_HMIP_ADDRESS_ACTIVE=""
  HM_HMIP_SERIAL=""
  HM_HMIP_SGTIN=""
  HM_HMIP_VERSION=""

  # if the user has configured a HB-RF-ETH ip address
  # in /etc/config/hb_rf_eth we try to load the corresponding
  # kernel module
  HB_RF_ETH_ADDRESS=$(head -1 /etc/config/hb_rf_eth 2>/dev/null | tr -d '[:space:]')
  if [[ -n "${HB_RF_ETH_ADDRESS}" ]]; then
    if [[ ! -e /sys/module/hb_rf_eth/parameters/connect ]]; then

      # load the hb_rf_eth kernel module
      if ! grep -Eq "^hb_rf_eth " /proc/modules; then
        modprobe -q hb_rf_eth
      fi

      # wait until kernel module is loaded
      for try in $(seq 1 30); do
        echo -n "."

        if [[ -e /sys/module/hb_rf_eth/parameters/connect ]]; then
          break
        fi
        sleep 1
      done
    fi

    # try to connect to the HB-RF-ETH
    for try in $(seq 1 30); do
      echo -n "."

      if [[ -e /sys/class/hb-rf-eth/hb-rf-eth/connect ]]; then
        echo -n "${HB_RF_ETH_ADDRESS}" >/sys/class/hb-rf-eth/hb-rf-eth/connect 2>/dev/null && break
      else
        echo -n "${HB_RF_ETH_ADDRESS}" >/sys/module/hb_rf_eth/parameters/connect 2>/dev/null && break
      fi
      sleep 1
    done
  fi

  RF_DEVNODES=
  # check for supported raw-uart hardware and add the
  # corresponding raw-uart device to RF_DEVNODES
  if ! grep -iq hmul /sys/bus/usb/devices/*/product 2>/dev/null; then
    # identify which raw-uart devices we have
    for dev in /sys/class/raw-uart/*; do
      dev="$(basename "${dev}")"
      if [[ -c "/dev/${dev}" ]]; then
        echo -n "."
        RF_DEVNODES="${RF_DEVNODES} ${dev}"
      fi
    done
  fi

  # if we don't have any raw-uart device we add the boards'
  # own serial devices
  if [[ -z "${RF_DEVNODES}" ]]; then

    case "${HM_HOST}" in
      # RaspberryPi
      rpi*)
        [[ -c /dev/ttyAMA0 ]] && RF_DEVNODES="ttyAMA0"
      ;;

      # ASUS Tinkerboard
      tinkerboard*)
        [[ -c /dev/ttyS1 ]] && RF_DEVNODES="ttyS1"
      ;;
    esac

    # also set tty device to low_latency mode (if possible)
    if [[ -n "${RF_DEVNODES}" ]]; then
      echo -n "."
      /bin/setserial "/dev/${RF_DEVNODES}" low_latency >/dev/null 2>&1
    fi
  fi

  # check if a HmIP-RFUSB (1b1f:c020) USB stick is connected
  # (only necessary for OCI/Docker/HA-Addon platform)
  if [[ "${HM_HOST}" == "oci" ]]; then
    id_to_dev() {
      find "$(grep -l "PRODUCT=$(printf "%x/%x" "0x${1%:*}" "0x${1#*:}")" \
              /sys/bus/usb/devices/[0-9]*:*/uevent 2>/dev/null | sed 's,uevent$,,')" \
        /dev/null -name dev -o -name dev_id 2>/dev/null |
      sed 's,[^/]*$,uevent,' |
      xargs sed -n -e s,DEVNAME=,,p -e s,INTERFACE=,,p
    }
    RFUSB_DEVNODE=$(id_to_dev 1b1f:c020)

    if [[ -n "${RFUSB_DEVNODE}" ]]; then
      echo -n "."
      RF_DEVNODES="${RFUSB_DEVNODE} ${RF_DEVNODES}"
      HM_HMIP_DEVTYPE="USB"
    fi
  fi

  # check if a HM-CFG-USB-2 (1b1f:c00f) USB stick is connected
  # and HMRF_DEV accordingly
  if lsusb 2>/dev/null | grep -q 1b1f:c00f; then
    HM_HMRF_DEV="HM-CFG-USB-2"
    HM_HMRF_SERIAL=$(cat /sys/bus/usb/devices/*/serial | grep -E '^[A-Z]{3}[0-9]{7}.*')
    HM_HMRF_DEVNODE=""
    HM_HMRF_DEVTYPE="USB"
    echo -n "."
  fi

  # strip leading/trailing spaces from RF_DEVNODES
  RF_DEVNODES=$(echo "${RF_DEVNODES}" | xargs)
}

# function to reset rf hardware
reset_rf_hardware() {

  # walk through our recognized device nodes and perform a
  # rf module reset
  for dev in ${RF_DEVNODES}; do

    echo -n "."

    # check if this is a raw-uart device and if so we use
    # the reset_radio_module sysfs node for the module reset
    if [[ -e "/sys/class/raw-uart/${dev}/reset_radio_module" ]]; then
      echo 1 >"/sys/class/raw-uart/${dev}/reset_radio_module" 2>/dev/null
    fi

  done

  # give the RF module some time to stabilize after the reset
  sleep 2
}

# function to read out all relevant rf module parameters (serial, firmware
# version, rf-addresses, sgtin, etc.)
query_rf_parameters() {

  # status for copro reset
  RF_COPRO_RESET_PERFORMED=false

  # walk through our recognized device nodes and query for
  # all rf module relevant parameters
  for dev in ${RF_DEVNODES}; do

    echo -n "."

    # use detect_radio_module to retrieve all relevant info from
    # the rf-module
    if [[ -c /dev/${dev} ]]; then
      if RF_INFO=$(/bin/detect_radio_module "/dev/${dev}"); then

        echo -n "."

        RF_HARDWARE=$(echo -n "${RF_INFO}" | cut -d' ' -f1 | tr '[:lower:]' '[:upper:]')

        # check if this is a factory reset and if so we go and factory reset
        # the rf module
        if [[ -r /usr/local/.doCoproFactoryReset ]]; then
          if [[ "${RF_HARDWARE}" == "RPI-RF-MOD" ]] ||
             [[ "${RF_HARDWARE}" == "HMIP-RFUSB" ]] ||
             [[ "${RF_HARDWARE}" == "HMIP-RFUSB-TK" ]]; then
            ARCH=$(uname -m)
            JAVAOPT=

            # shellcheck disable=SC2086
            /usr/bin/timeout 20 /opt/java/bin/java -Dos.arch=${ARCH} ${JAVAOPT} -Dgnu.io.rxtx.SerialPorts="/dev/${dev}" -jar /opt/HmIP/hmip-copro-update.jar -p "/dev/${dev}" -r >/dev/null 2>&1
            sleep 2
            # reload RF_INFO
            if ! RF_INFO=$(/bin/detect_radio_module "/dev/${dev}"); then
              echo -n "ERROR: /dev/${dev}: "
              break
            fi

            RF_COPRO_RESET_PERFORMED=true
          elif [[ "${RF_HARDWARE}" == "HM-MOD-RPI-PCB" ]]; then
            RF_COPRO_RESET_PERFORMED=true
          fi
        fi

        # get all RF parameters
        RF_SERIAL=$(echo -n "${RF_INFO}" | cut -d' ' -f2)
        RF_HMIP_SGTIN=$(echo -n "${RF_INFO}" | cut -d' ' -f3)
        RF_HMRF_ADDRESS=$(echo -n "${RF_INFO}" | cut -d' ' -f4)
        RF_HMIP_ADDRESS=$(echo -n "${RF_INFO}" | cut -d' ' -f5)
        RF_VERSION=$(echo -n "${RF_INFO}" | cut -d' ' -f6)

        # now check if we have a BidCos-RF/HmRF device already
        # set and if not use this device for BidCos-RF/HmRF
        if [[ -n "${RF_HMRF_ADDRESS}" ]] &&
           [[ -n "${RF_SERIAL}" ]] &&
           [[ -z "${RFUSB_DEVNODE}" ]] &&
           [[ "${RF_HARDWARE}" != "HMIP-RFUSB-TK" ]]; then

          # prefer a HM-MOD-RPI-PCB for HmRF stuff in case
          # we already have a setup HMRF device
          if [[ -z "${HM_HMRF_DEV}" ]] ||
             { [[ "${RF_HARDWARE}" == "HM-MOD-RPI-PCB" ]] &&
               [[ "${HM_HMRF_DEV}" == "RPI-RF-MOD" ]]; }; then

            # there might be situations where the RF_HMRF_ADDRESS
            # returned by detect_radio_module might be 0x000000 while
            # the rf module might still be HMRF capable. Thus we need
            # to generate a random RF address between 0xFF0000-0xFFFFFE
            if [[ "${RF_HMRF_ADDRESS}" == "0x000000" ]]; then
              RF_HMRF_ADDRESS=$(awk -v r=${RANDOM} 'BEGIN{srand(r);printf("0x%X", int(rand()*(0xFFFFFE-0xFF0000))+0xFF0000) }')
            fi

            HM_HMRF_DEV=${RF_HARDWARE}
            HM_HMRF_DEVNODE="/dev/${dev}"
            HM_HMRF_ADDRESS=${RF_HMRF_ADDRESS}
            HM_HMRF_SERIAL=${RF_SERIAL}
            HM_HMRF_VERSION=${RF_VERSION}

            if [[ -e /sys/class/raw-uart/${dev}/device_type ]]; then
              HM_HMRF_DEVTYPE=$(cat "/sys/class/raw-uart/${dev}/device_type")
            elif [[ -z "${HM_HMRF_DEVTYPE}" ]]; then
              HM_HMRF_DEVTYPE=GPIO
            fi
          fi
        fi

        # now check if we have a HmIP device already
        # set and if not use this device for HmIP
        if [[ -n "${RF_HMIP_ADDRESS}" ]] &&
           [[ "${RF_HMIP_ADDRESS}" != "0x000000" ]] &&
           [[ -n "${RF_SERIAL}" ]]; then

          # prefer a RPI-RF-MOD / HmIP-RFUSB for HmIP stuff in case
          # we already have a setup HMIP device
          if [[ -z "${HM_HMIP_DEV}" ]] ||
             { [[ "${RF_HARDWARE}" == "RPI-RF-MOD" ]] &&
               [[ "${HM_HMIP_DEV}" == "HM-MOD-RPI-PCB" ]]; }; then

            HM_HMIP_DEV=${RF_HARDWARE}
            HM_HMIP_DEVNODE="/dev/${dev}"
            HM_HMIP_ADDRESS=${RF_HMIP_ADDRESS}
            HM_HMIP_SERIAL=${RF_SERIAL}
            HM_HMIP_SGTIN=${RF_HMIP_SGTIN}
            HM_HMIP_VERSION=${RF_VERSION}

            if [[ -e /sys/class/raw-uart/${dev}/device_type ]]; then
              HM_HMIP_DEVTYPE=$(cat "/sys/class/raw-uart/${dev}/device_type")
            elif [[ -z "${HM_HMIP_DEVTYPE}" ]]; then
              HM_HMIP_DEVTYPE=GPIO
            fi
          fi
        fi
      fi
    fi

  done

  # cleanup coprocessor status file
  if [[ "${RF_COPRO_RESET_PERFORMED}" == "true" ]]; then
    rm -f /usr/local/.doCoproFactoryReset
  fi

  #####################################
  # now we check if we have a RPI-RF-MOD connected to
  # a HB-RF-USB/HB-RF-USB-2/HB-RF-ETH and if so we have to setup its
  # led driver correctly.
  if [[ "${HM_HMRF_DEV}" == "RPI-RF-MOD" ]] &&
    echo "${HM_HMRF_DEVTYPE}" | grep -qi HB-RF-; then
    HBRF_DEVNODE=$(basename "${HM_HMRF_DEVNODE}")
  elif [[ "${HM_HMIP_DEV}" == "RPI-RF-MOD" ]] &&
    echo "${HM_HMIP_DEVTYPE}" | grep -qi HB-RF-; then
    HBRF_DEVNODE=$(basename "${HM_HMIP_DEVNODE}")
  fi

  if [[ -n "${HBRF_DEVNODE}" ]]; then
    # get the RED/GREEN/BLUE pin ids
    RED_GPIO_PIN=$(cat "/sys/class/raw-uart/${HBRF_DEVNODE}/red_gpio_pin")
    GREEN_GPIO_PIN=$(cat "/sys/class/raw-uart/${HBRF_DEVNODE}/green_gpio_pin")
    BLUE_GPIO_PIN=$(cat "/sys/class/raw-uart/${HBRF_DEVNODE}/blue_gpio_pin")

    # make sure to unbind the gpio-leds driver first
    if [[ -e /sys/class/leds/rpi_rf_mod:blue ]]; then
      if [[ -e /sys/bus/platform/drivers/leds-gpio/gpio-leds ]]; then
        GPIO_LEDS=gpio-leds
      else
        GPIO_LEDS=leds
      fi

      echo ${GPIO_LEDS} >/sys/bus/platform/drivers/leds-gpio/unbind 2>/dev/null
    fi

    # load the rpi_rf_mod_led kernel module
    if ! grep -Eq "^rpi_rf_mod_led " /proc/modules; then
      modprobe -q rpi_rf_mod_led red_gpio_pin="${RED_GPIO_PIN}" green_gpio_pin="${GREEN_GPIO_PIN}" blue_gpio_pin="${BLUE_GPIO_PIN}"
    fi

    # rebind the gpio-leds so that numeration of the rpi-rf-mod leds are correct
    if [[ -n "${GPIO_LEDS}" ]] &&
       [[ ! -e /sys/bus/platform/drivers/leds-gpio/${GPIO_LEDS} ]]; then
      echo "${GPIO_LEDS}" >/sys/bus/platform/drivers/leds-gpio/bind 2>/dev/null
    fi

    # load the dummy rx8130 kernel module because an RPI-RF-MOD
    # is identified by having a rx8130 module loaded
    if ! grep -Eq "_rx8130 " /proc/modules; then
      modprobe -q dummy_rx8130
    fi

    # now we have to restart hss_led because hss_led is usually started earlier
    if [[ -f /var/run/hss_led.pid ]]; then
      start-stop-daemon -K -q -p /var/run/hss_led.pid
      if [[ -x /bin/hss_led ]]; then
        sleep 2
        if [[ "${HM_HOST}" =~ oci\|lxc ]]; then
          start-stop-daemon -S -q -b -m -c hssled:hssled -p /var/run/hss_led.pid --exec /bin/hss_led -- -l 6
        else
          start-stop-daemon -S -q -b -m -p /var/run/hss_led.pid --exec /bin/hss_led -- -l 6
        fi
      fi
    fi
  fi

  #####################################
  # we check if there are already some active bidcos rf address
  # or hmip address defined in /etc/config/ids and /etc/config/hmip_address.conf
  # or if this is a fresh installation and we have to use the default rf addresses
  # we queried above

  # get/set active bidcos rf address
  if [[ -f /etc/config/ids ]]; then
    HM_HMRF_ADDRESS_ACTIVE=$(grep -i BidCoS-Address /etc/config/ids 2>/dev/null | tr -d '[:space:]' | cut -d= -f2)

    # if HM_HMRF_ADDRESS_ACTIVE is empty or 0 (0x000000) we use HM_HMRF_ADDRESS as fallback
    # and also remove /etc/config/ids because it might contain an invalid
    # rf address
    if [[ -z "${HM_HMRF_ADDRESS_ACTIVE}" ]] ||
       [[ "${HM_HMRF_ADDRESS_ACTIVE}" == "0" ]] ||
       [[ "${HM_HMRF_ADDRESS_ACTIVE}" == "0x000000" ]]; then
      HM_HMRF_ADDRESS_ACTIVE=${HM_HMRF_ADDRESS}
      if [[ -e /etc/config/ids ]]; then
        mv -f /etc/config/ids "/etc/config/ids_old-$(date +%Y%m%d-%H%M%S)"
      fi
    fi
  else
    HM_HMRF_ADDRESS_ACTIVE=${HM_HMRF_ADDRESS}
  fi

  # get/set active hmip rf address
  if [[ -f /etc/config/hmip_address.conf ]]; then
    HM_HMIP_ADDRESS_ACTIVE="0x$(grep -i Adapter.1.Address /etc/config/hmip_address.conf 2>/dev/null | tr -d '[:space:]' | cut -d= -f2)"
  else
    HM_HMIP_ADDRESS_ACTIVE=${HM_HMIP_ADDRESS}
  fi

  #####################################
  # save all main info about the RF module

  # prefer to use a HM_HMIP_SERIAL with the last
  # 10 chars of the SGTIN because that is the serial
  # of the RF module registered at eQ3
  if [[ -n "${HM_HMIP_SERIAL}" ]]; then
    echo -n "${HM_HMIP_SERIAL}" >/var/board_serial
  elif [[ -n "${HM_HMRF_SERIAL}" ]]; then
    echo -n "${HM_HMRF_SERIAL}" >/var/board_serial
  else
    # fallback to put the mac in /var/board_serial
    MAC=$(cat /sys/class/net/"$(ip route show default | awk '/default/ {print $5}')"/address)
    echo -n "$(echo "${MAC}" | tr -d : | tail -c 10)" >/var/board_serial
  fi

  if [[ -n "${HM_HMIP_SGTIN}" ]]; then
    echo -n "${HM_HMIP_SGTIN}" >/var/board_sgtin
  fi

  # output BidCos/HmIP specific infos
  echo -n "${HM_HMRF_SERIAL}" >/var/rf_board_serial
  echo -n "${HM_HMRF_ADDRESS}" >/var/rf_address
  echo -n "${HM_HMRF_VERSION}" >/var/rf_firmware_version
  echo -n "${HM_HMIP_SERIAL}" >/var/hmip_board_serial
  echo -n "${HM_HMIP_VERSION}" >/var/hmip_firmware_version
  echo -n "${HM_HMIP_ADDRESS}" >/var/hmip_address
  echo -n "${HM_HMIP_SGTIN}" >/var/hmip_board_sgtin
}

start() {
  echo -n "Identifying Homematic RF-Hardware: "

  # source all data from /var/hm_mode
  [[ -r /var/hm_mode ]] && . /var/hm_mode

  identify_rf_hardware
  reset_rf_hardware
  query_rf_parameters

  # save all HM_ env variables
  set | grep '^HM_' >/var/hm_mode

  # output some status information
  echo -n "HmRF: "
  [[ -n "${HM_HMRF_DEV}" ]] && echo -n "${HM_HMRF_DEV}/${HM_HMRF_DEVTYPE}, " || echo -n "n/a, "
  echo -n "HmIP: "
  [[ -n "${HM_HMIP_DEV}" ]] && echo -n "${HM_HMIP_DEV}/${HM_HMIP_DEVTYPE}, " || echo -n "n/a, "
  echo "OK"
}

stop() {
  # send the coprocessor into the bootloader mode to stop it from
  # receiving any further events
  [[ -r /var/hm_mode ]] && . /var/hm_mode

  if [[ -n "${HM_HMRF_DEVNODE}" ]]; then
    /usr/bin/timeout 120 /bin/eq3configcmd update-coprocessor -p "${HM_HMRF_DEVNODE}" -bl -l 1
  fi

  if [[ -n "${HM_HMIP_DEVNODE}" ]] &&
     [[ "${HM_HMIP_DEVNODE}" != "${HM_HMRF_DEVNODE}" ]]; then
    /usr/bin/timeout 120 /bin/eq3configcmd update-coprocessor -p "${HM_HMIP_DEVNODE}" -bl -l 1
  fi

  # for the docker/OCI based platforms it might be a good
  # idea to try to unload the kernel modules which we
  # loaded ourself here so that everything is cleared once
  # the docker container stopped.

  if [[ "${HM_HMRF_DEV}" == "RPI-RF-MOD" ]] &&
     echo "${HM_HMRF_DEVTYPE}" | grep -qi HB-RF-; then
    HBRF_DEVNODE=$(basename "${HM_HMRF_DEVNODE}")
  elif [[ "${HM_HMIP_DEV}" == "RPI-RF-MOD" ]] &&
       echo "${HM_HMIP_DEVTYPE}" | grep -qi HB-RF-; then
    HBRF_DEVNODE=$(basename "${HM_HMIP_DEVNODE}")
  fi

  if [[ -n "${HBRF_DEVNODE}" ]]; then
    # unload dummy_rx8130 kernel module
    if grep -Eq "^dummy_rx8130 " /proc/modules; then
      rmmod dummy_rx8130 2>/dev/null
    fi

    # unload rpi_rf_mod_led kernel module
    if grep -Eq "^rpi_rf_mod_led " /proc/modules; then
      rmmod rpi_rf_mod_led 2>/dev/null
    fi
  fi

  # make sure the hb-rf-eth kernel module will stop trying to connect
  # to an associated HB-RF-ETH or otherwise a host machine (in case of
  # a docker environment) will continue to connect to the HB-RF-ETH
  # even thought e.g. the docker is stopped.
  if [[ -f "/etc/config/hb_rf_eth" ]] &&
     [[ -f "/sys/module/hb_rf_eth/parameters/connect" ]]; then
    # since the hb-rf-eth does not have a disconnect parameter
    # we send a simple '-' which should disconnect it.
    echo - >/sys/module/hb_rf_eth/parameters/connect 2>/dev/null

    # unload hb-rf-eth module as well
    rmmod hb-rf-eth 2>/dev/null
  fi
}

restart() {
  stop
  start
}

case "$1" in
  start)
    start
  ;;
  stop)
    stop
  ;;
  restart|reload)
    restart
  ;;
  *)
    echo "Usage: $0 {start|stop|restart}"
    exit 1
esac

exit 0
